import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import re
import seaborn as sns
import logging

from collections import Counter
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline

from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, roc_curve, confusion_matrix


# 关闭 Matplotlib 所有非错误级别的日志
logging.getLogger('matplotlib').setLevel(logging.ERROR)

# 设置中文显示（替换为系统存在的中文字体）
plt.rcParams["font.family"] = ["SimSun", "Microsoft YaHei", "Heiti TC", "WenQuanYi Micro Hei"]
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

# 读取豆瓣电影数据
df = pd.read_csv("douban_movies.csv")

# 显示前5行和数据信息
print(df.info())
print(df.head())

# 统一列名为小写，便于识别
df.columns = [c.strip().lower() for c in df.columns]

# 关键列
rating_col = "豆瓣评分"
year_col = "上映日期"
genre_col = "类型"
country_col = "制片国家/地区"
duration_col = "片长"

# 类型拆分函数
def split_multi(x):
    if pd.isna(x):
        return []
    return [p.strip() for p in re.split(r"[,/|，、\s]+", str(x)) if p.strip()]

# 年份数字提取
df[year_col] = df[year_col].astype(str).str.extract(r"(\d{4})").astype(float)

# 片长提取分钟数
df[duration_col] = df[duration_col].astype(str).str.extract(r"(\d+)").astype(float)

# 统计类型出现频次
cnt = Counter([g for lst in df[genre_col].dropna().apply(split_multi) for g in lst])
top_genres = [g for g, _ in cnt.most_common(12)]

# 为前12种类型生成多热编码特征
for g in top_genres:
    df[f"genre_{g}"] = df[genre_col].apply(lambda s: 1 if g in split_multi(s) else 0)

# 评分分布可视化
plt.figure()
df[rating_col].plot(kind="hist", bins=30, title="豆瓣评分分布")
plt.xlabel("豆瓣评分")
plt.ylabel("电影数量")
plt.show()

# 年份与平均评分趋势
yearly = df.groupby(year_col)[rating_col].mean()
yearly.plot(kind="line", title="年份与平均评分变化趋势")
plt.ylabel("平均评分")
plt.show()


# 设置分类阈值
threshold = 9.1
df["high_rating"] = (df[rating_col] >= threshold).astype(int)

# 数值与类别特征
numeric_features = [year_col, duration_col] + [c for c in df.columns if c.startswith("genre_")]
categorical_features = [country_col]

# 特征矩阵与标签
X = df[numeric_features + categorical_features]
y = df["high_rating"]

# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

# 数值预处理：缺失值填充 + 标准化
numeric_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler())
])

# 类别预处理：填充 + 独热编码
categorical_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore"))
])

# 列变换组合
preprocess = ColumnTransformer([
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features)
])


# 检查特征与评分的相关性
corr = df[[rating_col, duration_col] + [c for c in df.columns if c.startswith("genre_")]].corr()
plt.figure(figsize=(10,6))
sns.heatmap(corr, cmap="coolwarm", annot=False)
plt.title("特征与豆瓣评分相关性热力图")
plt.show()


# 已在 2.3 实现了类型的多热编码（genre_x）
# 也可以添加导演、国家等特征
print("使用的特征列：", X.columns.tolist())

# 定义五种模型
models = {
    "LogisticRegression": LogisticRegression(max_iter=1000),
    "SVC(RBF)": SVC(probability=True),
    "RandomForest": RandomForestClassifier(n_estimators=300, random_state=42),
    "GradientBoosting": GradientBoostingClassifier(random_state=42),
    "KNN": KNeighborsClassifier(n_neighbors=15)
}

results = []
plt.figure()

for name, clf in models.items():
    pipe = Pipeline([("preprocess", preprocess), ("clf", clf)])
    pipe.fit(X_train, y_train)
    y_pred = pipe.predict(X_test)
    y_proba = pipe.predict_proba(X_test)[:, 1]

    acc = accuracy_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    auc = roc_auc_score(y_test, y_proba)
    tn, fp, fn, tp = confusion_matrix(y_test, y_pred).ravel()

    results.append([name, acc, f1, auc, tn, fp, fn, tp])

    fpr, tpr, _ = roc_curve(y_test, y_proba)
    plt.plot(fpr, tpr, label=f"{name} (AUC={auc:.3f})")

plt.plot([0, 1], [0, 1], '--')
plt.legend()
plt.title("五种模型ROC曲线对比")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.show()

# 输出结果表格
results_df = pd.DataFrame(results, columns=["Model", "Accuracy", "F1", "ROC_AUC", "TN", "FP", "FN", "TP"])
print(results_df.sort_values(by="ROC_AUC", ascending=False))


# 模型结果排序展示
best = results_df.sort_values(by="ROC_AUC", ascending=False).iloc[0]
print("最佳模型：", best["Model"])
print(f"准确率：{best['Accuracy']:.3f}, F1值：{best['F1']:.3f}, AUC：{best['ROC_AUC']:.3f}")

# 混淆矩阵可视化
plt.figure(figsize=(4,3))
sns.heatmap(confusion_matrix(y_test, pipe.predict(X_test)), annot=True, fmt="d", cmap="Blues")
plt.title(f"最佳模型 {best['Model']} 混淆矩阵")
plt.xlabel("预测标签")
plt.ylabel("真实标签")
plt.show()
